﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;

using zijian666.Converts.Abstractions;
using zijian666.Converts.Core;

namespace zijian666.Converts.Extensions;

/// <summary>
/// 关于类型 <seealso cref="Type"/> 的扩展方法
/// </summary>
public static class TypeExtensions
{
    /// <summary>
    /// 安全的获取程序集中可以被导出的类型
    /// </summary>
    /// <param name="assembly"></param>
    /// <returns></returns>
    public static IEnumerable<Type> SafeGetTypes(this Assembly assembly)
    {
        if (assembly is null)
        {
            return Type.EmptyTypes;
        }
        try
        {
            return assembly.GetTypes();
        }
        catch (ReflectionTypeLoadException ex)
        {
            return ex.Types;
        }
        catch
        {
            return Type.EmptyTypes;
        }
    }

    /// <summary>
    /// 判断类型是否可被实例化 <para/>
    /// 是值类型, 或 至少有一个公开构造函数的非抽象类/非静态类/非泛型定义类
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public static bool Instantiable(this Type type) =>
        type.IsValueType
        || (type.IsClass && !type.IsAbstract && !type.IsGenericTypeDefinition && type.GetConstructors().Length > 0);

    /// <summary>
    /// 获取当前类型根据指定泛型定义类型约束导出的泛型参数
    /// </summary>
    /// <param name="type"> 测试类型 </param>
    /// <param name="defineType"> 泛型定义类型 </param>
    /// <param name="inherit"> 是否检测被测试类型的父类和接口 </param>
    /// <returns></returns>
    public static Type[]? GetGenericArguments(this Type type, Type defineType, bool inherit = true)
    {
        if (defineType.IsAssignableFrom(type)) //2个类本身存在继承关系
        {
            return type.GetGenericArguments();
        }
        if (defineType.IsGenericType == false)
        {
            return null; //否则如果definer不是泛型类,则不存在兼容的可能性
        }
        if (defineType.IsGenericTypeDefinition == false)
        {
            defineType = defineType.GetGenericTypeDefinition(); //获取定义类型的泛型定义
        }
        if (type.IsGenericType)
        {
            if (type.IsGenericTypeDefinition)
            {
                return null; //tester是泛型定义类型,无法兼容
            }
            //获取2个类的泛型参数
            var arg1 = ((TypeInfo)defineType).GenericTypeParameters;
            var arg2 = type.GetGenericArguments();
            //判断2个类型的泛型参数个数
            if (arg1.Length == arg2.Length)
            {
                //判断definer 应用 tester泛型参数 后的继承关系
                if (defineType.MakeGenericType(arg2).IsAssignableFrom(type))
                {
                    return arg2;
                }
            }
        }
        if (inherit == false)
        {
            return null;
        }
        //测试tester的父类是否被definer兼容
        var baseType = type.BaseType;
        while ((baseType != typeof(object)) && (baseType is not null))
        {
            var result = GetGenericArguments(baseType, defineType, false);
            if (result is not null)
            {
                return result;
            }
            baseType = baseType.BaseType;
        }
        //测试tester的接口是否被definer兼容
        foreach (var @interface in type.GetInterfaces())
        {
            var result = GetGenericArguments(@interface, defineType, false);
            if (result is not null)
            {
                return result;
            }
        }
        return null;

    }

    /// <summary>
    /// 枚举指定类型的所有父类, 不包括 <seealso cref="object"/>
    /// </summary>
    /// <param name="type"> 需要枚举父类的类型 </param>
    /// <returns> </returns>
    public static IEnumerable<Type> EnumerateBaseTypes(this Type type)
    {
        var baseType = type?.BaseType ?? typeof(object);
        while (baseType != typeof(object))
        {
            yield return baseType;
            baseType = baseType.BaseType ?? typeof(object);
        }
    }

    /// <summary>
    /// 类型名称缓存
    /// </summary>
    private static readonly ConcurrentDictionary<Type, string> _typeNames = new();

    /// <summary>
    /// 元组类型的接口,通过这个接口判断类型是否是元组类型
    /// </summary>
    private static readonly Type _valueTupleType = Type.GetType("System.IValueTupleInternal", false, false);

    /// <summary>
    /// 获取类型名称的友好展现形式
    /// </summary>
    public static string GetFriendlyName(this Type type)
    {
        if (type is null)
        {
            return "`null`";
        }

        return _typeNames.GetOrAdd(type, t =>
        {
            var t2 = Nullable.GetUnderlyingType(t);
            if (t2 is not null)
            {
                return GetFriendlyName(t2) + "?";
            }
            if (t.IsPointer)
            {
                return GetFriendlyName(t.GetElementType()) + "*";
            }
            if (t.IsByRef)
            {
                return GetFriendlyName(t.GetElementType()) + "&";
            }
            if (t.IsArray)
            {
                return GetFriendlyName(t.GetElementType()) + "[]";
            }
            if (!t.IsGenericType)
            {
                return GetSimpleName(t);
            }
            string[] generic;
            if (t.IsGenericTypeDefinition) //泛型定义
            {
                generic = t.GetGenericArguments().Select(x => "").ToArray();
            }
            else
            {
                var infos = t.GetGenericArguments();
                generic = new string[infos.Length];
                for (var i = 0; i < infos.Length; i++)
                {
                    generic[i] = GetFriendlyName(infos[i]);
                }

                //这个表示元组类型
                if (_valueTupleType?.IsAssignableFrom(t) ?? false)
                {
                    return "(" + string.Join(", ", generic) + ")";
                }
            }
            return GetSimpleName(t) + "<" + string.Join(", ", generic) + ">";
        });
    }

    /// <summary>
    /// 获取指定类型的简单名称
    /// </summary>
    /// <param name="t">指定类型</param>
    /// <returns></returns>
    private static string GetSimpleName(Type t)
    {
        string name;
        if (t.ReflectedType is null)
        {
            switch (t.Namespace)
            {
                case "System":
                    switch (t.Name)
                    {
                        case "Boolean":
                            return "bool";
                        case "Byte":
                            return "byte";
                        case "Char":
                            return "char";
                        case "Decimal":
                            return "decimal";
                        case "Double":
                            return "double";
                        case "Int16":
                            return "short";
                        case "Int32":
                            return "int";
                        case "Int64":
                            return "long";
                        case "SByte":
                            return "sbyte";
                        case "Single":
                            return "float";
                        case "String":
                            return "string";
                        case "Object":
                            return "object";
                        case "UInt16":
                            return "ushort";
                        case "UInt32":
                            return "uint";
                        case "UInt64":
                            return "ulong";
                        case "Guid":
                            return "Guid";
                        case "Void":
                            return "void";
                        default:
                            name = t.Name;
                            break;
                    }
                    break;
                case null:
                case "System.Collections":
                case "System.Collections.Generic":
                case "System.Data":
                case "System.Text":
                case "System.IO":
                case "System.Collections.Specialized":
                    name = t.Name;
                    break;
                default:
                    if (t.Namespace.StartsWith("blqw.Kanai", StringComparison.Ordinal))
                    {
                        name = t.Name;
                    }
                    else
                    {
                        name = $"{t.Namespace}.{t.Name}";
                    }
                    break;
            }
        }
        else
        {
            name = $"{GetSimpleName(t.ReflectedType)}.{t.Name}";
        }
        var index = name.LastIndexOf('`');
        if (index > -1)
        {
            name = name.Remove(index);
        }
        return name;
    }

    /// <summary>
    /// 判断一个类型是否是系统类型, 除了基元类型(<seealso cref="Type.IsPrimitive"/>)外还包含
    /// <seealso cref="string"/>,
    /// <seealso cref="DateTime"/>,
    /// <seealso cref="Guid"/>,
    /// <seealso cref="TimeSpan"/>,
    /// <seealso cref="DateTimeOffset"/>,
    /// <seealso cref="decimal"/>,
    /// <seealso cref="DBNull"/>,
    /// 还有可空值类型的实际类型
    /// </summary>
    public static bool IsSystemType(this Type type)
    {
        if (type is null)
        {
            return false;
        }
        if (type.IsPrimitive)
        {
            return true;
        }
        return type == typeof(string)
            || type == typeof(DateTime)
            || type == typeof(Guid)
            || type == typeof(TimeSpan)
            || type == typeof(DateTimeOffset)
            || type == typeof(decimal)
            || type == typeof(DBNull)
            || typeof(Type).IsAssignableFrom(type)
            || IsSystemType(Nullable.GetUnderlyingType(type));
    }

    /// <summary>
    /// 部分类型优先级调整
    /// </summary>
    private static readonly Dictionary<Type, int> _priorities = new()
    {
        [typeof(IObjectProxy)] = 401,
        [typeof(IObjectReference)] = 400,
        [typeof(IFormatProvider)] = 300,
        [typeof(IDictionary<,>)] = 200,
        [typeof(IDictionary)] = 199,
        [typeof(IFormatterConverter)] = 102,
        [typeof(IFormattable)] = 101,
        [typeof(IConvertible)] = 100,
        [typeof(IEnumerable<>)] = 99,
        [typeof(IEnumerable)] = 98,
        [typeof(IEnumerator<>)] = 97,
        [typeof(IEnumerator)] = 96,
        [typeof(DynamicObject)] = 95
    };

    /// <summary>
    /// 获取 <seealso cref="Type" /> 的优先级
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    internal static int GetPriority(this Type type)
    {
        if (type is null)
        {
            return int.MinValue;
        }
        // 降低密封类的优先级
        if (type.IsSealed)
        {
            return int.MinValue + 1;
        }
        if (type.IsGenericType)
        {
            // 获取泛型的定义类型
            type = type.IsGenericTypeDefinition ? type : type.GetGenericTypeDefinition() ?? type;
        }
        return _priorities.TryGetValue(type, out var i) ? i : 100;
    }

    /// <summary>
    /// 确定类型 <paramref name="type"/> 与 <paramref name="defaultType"/> 和 <paramref name="interfaceType"/> 之间的匹配关系
    /// <br /> <paramref name="type"/> 与 <paramref name="defaultType"/> 相同 返回 <seealso cref="MacthedType.Equals"/>
    /// <br /> <paramref name="type"/> 是 <paramref name="defaultType"/> 的父类或接口 返回 <seealso cref="MacthedType.Acceptable"/>
    /// <br /> <paramref name="type"/> 不是 <paramref name="interfaceType"/> 的子类或实现 且 拥有无参的构造函数 返回 <seealso cref="MacthedType.TryCreate"/>
    /// <br /> 完全没有关系, 则返回 <seealso cref="MacthedType.NotMatched"/>
    /// </summary>
    internal static MacthedType MatchType(this Type type!!, Type defaultType!!, Type interfaceType!!)
    {
        if (type == defaultType)
        {
            return MacthedType.Equals;
        }
        if (type == typeof(object) || type == typeof(void) || type is null)
        {
            return MacthedType.NotMatched;
        }
        if (type.IsAssignableFrom(defaultType))
        {
            return MacthedType.Acceptable;
        }
        if (interfaceType.IsAssignableFrom(type) && type.IsClass && !type.IsAbstract && type.GetConstructor(Type.EmptyTypes) is not null)
        {
            return MacthedType.TryCreate;
        }
        return MacthedType.NotMatched;
    }
}
